
#include "ltm.h"
#include "stm.h"
#include "fMatrix.h"
#include <stdlib.h>
#define BUFDIM 50

/*TODO l'algorimo funziona solo se il numero di iterazioni globali LOS (in STM) rimane costante. Infatti ora come ora
 * una soluzione è stata intensificata a sufficienza sse e' stata in soluzione il 0.05% delle iterazioni LOS ( che se sono 1000 vuol dire 50
 * iterazioni, che non sono poche!). però se aad un certo punto le iterazioni LOS vengono impostate a 50 anche con una sola iterazione LOS
 * la soluzione secondo LTM e' gia' intensificata! secondo me questo 0.05 dovrebbe essere passato dall'utente e, se non viene passato,
 * dovrebbe essere 50.
 */

Psolution executeLTM(Pgraph pgraph, PLTMConfiguration configuration){

	int LTMiteration; //indica il numero di iterazioni LTM eseguite
	PLTMFrequencies fmatrices; //indica una serie di matrici delle sequenze ognuna per ogni camion
	int STMintensification; //indica quanto STM ha intensificato la soluzione che ha proposto
	int STMiteration; //indica quante iterazioni LOS sono state eseguite nell'ultimo executeSTM
	Psolution psolution; //indica la soluzione che LTM pensa finora ottima
	Psolution pbestSolution; //indica la migliore soluzione ottenuta fino a questo momento
	PtabooState ptabooState; //indica la struttura contenente tutti i taboo
	PvehiclePath path; //path del camion i-esimo
	int currentProfit; //profitto ottenuto dalla attuale soluzione STM
	int bestProfit; //profitto ottenuto dalla migliore soluzione STM
	int i; //variabile di supporto per i cicli
	char constructivebuffer[BUFDIM];
	char STMbuffer[BUFDIM];

	// ************ INIZIALIZZAZIONE *******************/
	bestProfit = 0;
	LTMiteration = 1;
	psolution = NULL;
	pbestSolution = NULL;
	ptabooState = initTabooState(pgraph->N, &configuration->vehicleNumber, 0);
	fmatrices = initLTMFrequencies(&configuration->vehicleNumber, pgraph->N);
	initSTMEngine(pgraph->T, pgraph->Q);

	// *************** CICLO BASE **********************/
	while (continueLTM(LTMiteration)){
		/*DEBUG
		printf("*************** ITERAZIONE %d *****************\n",LTMiteration);*/
		if (LTMiteration > 1){
			resetTabooState(ptabooState, 0);//pulisco l'intera tabooState dai taboo utilizzati da STM
		}
		psolution = buildConstructiveSolution(
			LTMiteration,
			&configuration->vehicleNumber,
			psolution,
			pgraph,
			ptabooState,
			fmatrices,
			STMiteration,
			STMintensification);
		/*DEBUG
		printf("SOLUZIONE COSTRUTTIVA\n");
		printVehicles(stdout,psolution);
		sprintf(constructivebuffer,"costruttiva_%d.bmp",LTMiteration);
		drawSolutionFromScratchDefaults(constructivebuffer,psolution,pgraph->depot);*/
		executeSTM(
			psolution,
			pgraph,
			ptabooState,
			&STMiteration,
			&STMintensification,
			configuration->stmConfiguration
		);
		currentProfit = getSolutionProfit(psolution);

		/*DEBUG
		printf("Soluzione secondo STM\n");
		printVehicles(stdout,psolution);
		sprintf(STMbuffer,"stm_%d.bmp",LTMiteration);
		drawSolutionFromScratchDefaults(STMbuffer,psolution,pgraph->depot);*/
		//aggiorno ora la tabella delle frequenze
		for (i=0; i<configuration->vehicleNumber; i++){
			path = psolution->states[i]->pvp;
			while ((path=path->next)->edge != NULL){
				addFMatrixValue(path->edge->id, fmatrices->vehicleMatrices[i], 1);
			}
		}
		//aggiorno la soluzione migliore fino ad ora incrontrata
		if (bestProfit > currentProfit){
			if (pbestSolution!=NULL){
				freeSolution(pbestSolution);
			}
			pbestSolution = cloneSolution(psolution);
			bestProfit = currentProfit;
		}

		/*DEBUG
		printLTMFrequencies(stdout,fmatrices);*/
		//aggiorno il contatore LTMIteration
		LTMiteration++;
	}
	return psolution;
}

Psolution buildConstructiveSolution(int LTMiteration, int* K, Psolution STMSolution, Pgraph pgraph, PtabooState ptabooState, PLTMFrequencies fmatrices, int STMiteration, int STMintensification){

	Psolution psolution;
	int i;
	PvehiclePath path;

	switch (LTMiteration){
	case 1: {//nell'iterazione 1 utilizzo la costruttiva a random
		psolution = initSolution(K, pgraph->N);
		buildSimpleRandomConstructive(pgraph, psolution, ptabooState);
		break;
	}
	case 2: {//nell'iterazione 2 utilizzo la costruttiva simple
		psolution = initSolution(K, pgraph->N);
		buildSimpleConstructive(pgraph, psolution, ptabooState);
		break;
	}
	default: {//iterazioni successive
		//decido se devo diversificare o intensificare
		if (STMintensification > LTM_INTENSIFICATION_STM_THRESHOLD*STMiteration){//diversifico
			printf("voglio diversificare!\n");
			buildDynamicConstructiveWithLowFrequencyEdges(
				pgraph,
				fmatrices,
				psolution,
				ptabooState,
				LTM_INTENSIFICATION_STM_THRESHOLD * STMiteration
			);
		}else{//intensifico
			printf("voglio intensificare!\n");
			psolution = STMSolution;
			//impongo che la soluzione passata da STM non possa variare per le prime iterazioni di STM
			for (i=0; i<*psolution->K; i++){
				path = psolution->states[i]->pvp;
				while ((path=path->next)->edge != NULL){
					setTaboo(
						ptabooState->tls[i],
						path->edge->id,
						(int)(LTM_INTENSIFICATION_STM_THRESHOLD * STMiteration),//la percentuale e' la stessa della soglia di intensificazione
						0
					);
				}
			}
		}
	}
	}
	return psolution;
}

void buildDynamicConstructiveWithLowFrequencyEdges(Pgraph pgraph, PLTMFrequencies matrixes, Psolution solution, PtabooState taboostate, int tenure){
	int i;
	for (i=0; i<*solution->K; i++){
		buildDynamicConstructiveWithLowFrequencyEdgesRecursive(
			pgraph->depot,
			pgraph,
			matrixes->vehicleMatrices[i],
			solution->gMatrix,
			i,
			solution->states[i],
			taboostate->tls[i],
			tenure
		);
	}
}

void buildDynamicConstructiveWithLowFrequencyEdgesRecursive(int node, Pgraph pgraph, PfMatrix fmatrix, PGMatrix gmatrix, int k, PvehicleState path, PtabooList taboolist, int tenure){
	int i;
	PvehiclePath edgelist = initVehiclePath();
	PvehiclePath tmp;
	EdgeID edge;
	edge.j = node;
	//prendo tutti i lati adiacenti al nodo "node" che 1) hanno bassa frequenza 2) non sono stati gia' raccolti
	for (edge.i=0; edge.i<(node-1); edge.i++){//ciclo sulle righe
		if (fmatrix->matrix[edge.i][edge.j] == fmatrix->minimum && !isEdgeGathered(gmatrix, &edge)){
			addEdgeToVehiclePath(&edge, edgelist);
		}
	}
	edge.i = node;
	for (edge.j=(node+1); edge.j<*fmatrix->N; edge.j++){ //ciclo sulle colonne
		if (fmatrix->matrix[edge.i][edge.j] == fmatrix->minimum && !isEdgeGathered(gmatrix, &edge)){
			addEdgeToVehiclePath(&edge, edgelist);
		}
	}
	/*DEBUG
	printf("archi adiacenti al nodo %d: ",node);
	printVehiclePath(stdout,edgelist);*/
	//per ognuno dei lati controllo che path+lato sia ancora TQOK. se non lo e' passo al prossimo lato
	tmp = edgelist;
	while ((tmp=tmp->next)->edge!=NULL){
		/*DEBUG
		if (isNodeUsedByEdgeID(tmp->edge->id,5)){
		printf("**********\n");
		printf("vincolo T: %d > %d\n",(path->tau+2*getEdgeGraph(pgraph,tmp->edge->id)->t),pgraph->T);
		printf("vincolo Q: %d > %d\n",(path->theta+getEdgeGraph(pgraph,tmp->edge->id)->d),pgraph->Q);
		printf("**********\n");
		}*/
		if (((path->tau + 2 * getEdgeGraph(pgraph, tmp->edge->id)->t) > pgraph->T) ||
			((path->theta + getEdgeGraph(pgraph, tmp->edge->id)->d) > pgraph->Q)){
			/*DEBUG
			printf("scarto ");
			printEdgeID(stdout,tmp->edge->id);
			printf("\n");*/
			continue;
		}
		/*DEBUG
		printf("aggiungo l'arco ");printEdgeID(stdout,tmp->edge->id); printf("\n");*/
		//se lo e' aggiungo al percorso il lato incriminato e richiamo getLowFrequencyEdgesRecursive
		//TODO in futuro si potrebbe creare una primitiva che dice anche quate volte passarci!
		addEdgeOp(
			tmp->edge->id,
			getEdgeGraph(pgraph, tmp->edge->id),
			path,
			gmatrix,
			k,
			taboolist,
			0,
			tenure,
			true
		);
		addEdgeOp(
			tmp->edge->id,
			getEdgeGraph(pgraph, tmp->edge->id),
			path,
			gmatrix,
			k,
			taboolist,
			0,
			tenure,
			false
		);
		/*DEBUG
		printVehicleState(stdout,path,k);*/
		buildDynamicConstructiveWithLowFrequencyEdgesRecursive(
			getEdgeIDOtherNode(tmp->edge->id, node),
			pgraph,
			fmatrix,
			gmatrix,
			k,
			path,
			taboolist,
			tenure
		);
	}
	//elimino tutto cio' che ho allocato
	freeVehiclePathHard(edgelist);
}

void generateCustomizedVehicleState(Pgraph pg){
	//parto dal deposito
	int depot = pg->depot;
	//per ogni
}

//TODO modificare il 5
//TODO testing
bool continueLTM(int LTMiteration){
	return LTMiteration<5;
}

void buildSimpleConstructive(Pgraph pg, Psolution sol, PtabooState tabooState){
	int k;//il camion da analizzare
	int i, j; //i due vertici che formano l'arco che si sta considerandi
	EdgeID currentEdge; //l'arco da considerare. Questo arco inizia sempre dal nodo deposito
	Weight currentWeight; //il peso dell'arco considerato
	Weight maxWeight; //il peso dell'arco migliore fino a questo momento trovato
	EdgeID maxEdge; //l'arco migliore fino a questo momento trovato

	maxEdge.i = pg->depot;
	maxEdge.j = 0;
	i= pg->depot;
	// considero ogni camion
	for (k=0; k<*(sol->K); k++){
		//resetto i campi temporanei
		maxWeight = 0;
		currentWeight = 0;
		//per ogni camion trova l'arco migliore. Per farlo fai passare tutti gli archi che partono dal deposito
		//non devo validare l'EdgeID perche' si verifica sempre i<j perche' i=0
		for (j=0; j<(*pg->N); j++){
			if (j==i){
				continue;
			}
			//creo un arco corretto
			currentEdge.i = i;
			currentEdge.j = j;
			validateEdgeID(&currentEdge);
			//controllo che l'arco non sia gia' stato soddisfatto da altre persone
			if (isEdgeGathered(sol->gMatrix, &currentEdge)){
				continue;
			}
			//verifico che l'eventuale percorso soddisfi i criteri TQOK
			if (!simpleIsTQOK(&pg->am[currentEdge.i][currentEdge.j], pg->T, pg->Q)){
				continue;
			}
			//TODO QUESTION questa valutazione è hardcoded, ok? non sta nella config?
			//valuto ora l'arco: se la valutazione e' la migliore fino a questo momento, aggiorno l'arco da scegliere, altrimenti vado avanti
			currentWeight = evaluateEdge(pg,&currentEdge, 0, 0, EVAL_SMART);
			if (currentWeight > maxWeight){
				maxWeight = currentWeight;
				maxEdge = currentEdge;
			}
		}
		//inserisco maxEdge nella soluzione
		addEdgeOp(
			&maxEdge,
			getEdgeGraph(pg, &maxEdge),
			getVehicleStateInSolution(sol, k),
			sol->gMatrix,
			k,
			tabooState->tls[k],
			0,
			LTM_SIMPLECONSTR_ADDTENURE,
			true);
		addEdgeOp(
			&maxEdge,
			getEdgeGraph(pg, &maxEdge),
			getVehicleStateInSolution(sol, k),
			sol->gMatrix,
			k,
			tabooState->tls[k],
			0,
			LTM_SIMPLECONSTR_ADDTENURE,
			false);
	}
}

void buildSimpleRandomConstructive(Pgraph pg, Psolution sol, PtabooState tabooState){
	int k;//il camion da analizzare
	EdgeID currentEdge; //l'arco da considerare. Questo arco inizia sempre dal nodo deposito

	currentEdge.i = pg->depot;
	currentEdge.j = 0;
	randomize();
	// considero ogni camion
	for (k=0; k<*(sol->K); k++){
		do{
			currentEdge.j = 1+rand()%(*pg->N-1);
			/*ARCO SCELTO
			printf("++++++++++++++++++++++++++++++++++++++++++\n");
			printf("edge: ");	printEdgeID(stdout,&currentEdge); printf("\n");
			printf("%s\n",simpleIsTQOK(getEdgeGraph(pg,&currentEdge),pg->T,pg->Q)==1?"true":"false");
			printf("%s\n",isEdgeGathered(sol->gMatrix,&currentEdge)==1?"true":"false");*/
		}while (!simpleIsTQOK(getEdgeGraph(pg, &currentEdge), pg->T, pg->Q) || isEdgeGathered(sol->gMatrix, &currentEdge));
		//inserisco currentEdge nella soluzione
		addEdgeOp(
			&currentEdge,
			getEdgeGraph(pg, &currentEdge),
			getVehicleStateInSolution(sol, k),
			sol->gMatrix,
			k,
			tabooState->tls[k],
			0,
			LTM_SIMPLECONSTR_ADDTENURE,
			true);
		addEdgeOp(
			&currentEdge,
			getEdgeGraph(pg, &currentEdge),
			getVehicleStateInSolution(sol, k),
			sol->gMatrix,
			k,
			tabooState->tls[k],
			0,
			LTM_SIMPLECONSTR_ADDTENURE,
			false);
	}
}

bool simpleIsTQOK(PedgeGraph edge, int T, int Q){
	return 2*edge->t <= T && edge->d <= Q;
}
